unit Helper.Config;

interface

uses
  JSON,
  Rtti;

type
  TConfig = class
  private
    class var fAppConfig: TConfig;
    class var fUserConfig: TConfig;
    class function GetApplicationName: string;
  private
    fSections: IJSONObject;
    fFilename: string;
    function getValue(Section, Key: string): TValue;
    property Values[Section, Key: string]: TValue read getValue;
  public
    class constructor Create;
    class destructor Destroy;
    class property App: TConfig read fAppConfig;
    class property User: TConfig read fUserConfig;
  public
    constructor Create(aFilename: string);
    destructor Destroy; override;

    procedure Write(aSection, aKey: string; aValue: string); overload;
    procedure Write(aSection, aKey: string; aValue: integer); overload;
    procedure Write(aSection, aKey: string; aValue: double); overload;
    procedure Write(aSection, aKey: string; aValue: boolean); overload;

    function ReadString(aSection, aKey: string; aDefault: string): string;
    function ReadInteger(aSection, aKey: string; aDefault: integer): integer;
    function ReadDouble(aSection, aKey: string; aDefault: double): double;
    function ReadBoolean(aSection, aKey: string; aDefault: boolean): boolean;

    procedure Load;
    procedure Save;
  end;

implementation

uses
  IOUtils,
  SysUtils,
  Helper.SpecialFolder;

{ TConfig }

class constructor TConfig.Create;
var
  path, fn: string;
begin
  path := TSpecialFolder.CommonAppData + '\' + GetApplicationName + '\';
  TDirectory.CreateDirectory(path);
  fn := path + 'settings.json';
  fAppConfig := TConfig.Create(fn);
  path := TSpecialFolder.UserAppData + '\' + GetApplicationName + '\';
  TDirectory.CreateDirectory(path);
  fn := path + 'settings.json';
  fUserConfig := TConfig.Create(fn);
end;

class destructor TConfig.Destroy;
begin
  fAppConfig.Free;
  fUserConfig.Free;
end;

constructor TConfig.Create(aFilename: string);
begin
  fFilename := aFilename;
  Load;
end;

destructor TConfig.Destroy;
begin
  Save;
  inherited;
end;

class function TConfig.GetApplicationName: string;
begin
  result := ExtractFilename(ParamStr(0));
  delete(result, length(result) - 3, 4); // Delete ".exe"
end;

function TConfig.getValue(Section, Key: string): TValue;
var
  jo: IJSONObject;
begin
  result := TValue.Empty;
  if fSections.HasKey(Section) then
  begin
    jo := fSections.GetJSONObject(Section);
    if jo.HasKey(Key) then
      result := jo.getValue(Key);
  end;
end;

procedure TConfig.Load;
var
  data: string;
begin
  if TFile.Exists(fFilename) then
  begin
    data := TFile.ReadAllText(fFilename);
    fSections := TJSON.NewObject(data);
  end
  else
    fSections := TJSON.NewObject;
end;

function TConfig.ReadBoolean(aSection, aKey: string; aDefault: boolean): boolean;
var
  v: TValue;
begin
  v := Values[aSection, aKey];
  if v.IsEmpty then
    result := aDefault
  else
    result := v.AsBoolean;
end;

function TConfig.ReadDouble(aSection, aKey: string; aDefault: double): double;
var
  v: TValue;
begin
  v := Values[aSection, aKey];
  if v.IsEmpty then
    result := aDefault
  else
    result := v.AsExtended;
end;

function TConfig.ReadInteger(aSection, aKey: string; aDefault: integer): integer;
var
  v: TValue;
begin
  v := Values[aSection, aKey];
  if v.IsEmpty then
    result := aDefault
  else
    result := v.AsInteger;
end;

function TConfig.ReadString(aSection, aKey, aDefault: string): string;
var
  v: TValue;
begin
  v := Values[aSection, aKey];
  if v.IsEmpty then
    result := aDefault
  else
    result := v.AsString;
end;

procedure TConfig.Save;
var
  data: string;
begin
  data := fSections.ToString;
  TFile.WriteAllText(fFilename, data);
end;

procedure TConfig.Write(aSection, aKey: string; aValue: double);
var
  jo: IJSONObject;
begin
  if not fSections.HasKey(aSection) then
    fSections.Put(aSection, TJSON.NewObject);
  jo := fSections.GetJSONObject(aSection);
  jo.Put(aKey, aValue);
end;

procedure TConfig.Write(aSection, aKey: string; aValue: boolean);
var
  jo: IJSONObject;
begin
  if not fSections.HasKey(aSection) then
    fSections.Put(aSection, TJSON.NewObject);
  jo := fSections.GetJSONObject(aSection);
  jo.Put(aKey, aValue);
end;

procedure TConfig.Write(aSection, aKey, aValue: string);
var
  jo: IJSONObject;
begin
  if not fSections.HasKey(aSection) then
    fSections.Put(aSection, TJSON.NewObject);
  jo := fSections.GetJSONObject(aSection);
  jo.Put(aKey, aValue);
end;

procedure TConfig.Write(aSection, aKey: string; aValue: integer);
var
  jo: IJSONObject;
begin
  if not fSections.HasKey(aSection) then
    fSections.Put(aSection, TJSON.NewObject);
  jo := fSections.GetJSONObject(aSection);
  jo.Put(aKey, aValue);
end;

end.
